import asyncio
import httpx
import time
from typing import Dict, Tuple
from sqlalchemy.orm import Session
from app.models.database.models import ServiceConfig, HealthCheck
from app.core.config.settings import settings
import logging

logger = logging.getLogger(__name__)


class HealthChecker:
    """健康检查服务"""

    def __init__(self):
        self.client = httpx.AsyncClient(
            timeout=httpx.Timeout(settings.default_timeout),
            limits=httpx.Limits(max_connections=100)
        )

    async def check_service_health(
            self,
            service: ServiceConfig,
            db: Session
    ) -> Dict[str, any]:
        """检查单个服务的健康状态"""
        start_time = time.time()
        health_result = {
            "service_id": service.id,
            "status": "unknown",
            "response_time": None,
            "http_status": None,
            "error_message": None
        }

        try:
            # 构建健康检查URL
            base_url = service.primary_url if service.is_active else service.backup_url
            health_url = f"{base_url.rstrip('/')}/{service.health_check_endpoint.lstrip('/')}"

            # 执行健康检查
            response = await self.client.get(health_url)
            response_time = (time.time() - start_time) * 1000  # 转换为毫秒

            health_result.update({
                "status": "healthy" if response.status_code == 200 else "unhealthy",
                "response_time": response_time,
                "http_status": response.status_code
            })

            if response.status_code != 200:
                health_result["error_message"] = f"HTTP {response.status_code}"

        except httpx.TimeoutException:
            health_result.update({
                "status": "timeout",
                "error_message": "Request timeout"
            })
        except httpx.ConnectError:
            health_result.update({
                "status": "unhealthy",
                "error_message": "Connection failed"
            })
        except Exception as e:
            health_result.update({
                "status": "error",
                "error_message": str(e)
            })

        # 记录健康检查结果
        self._save_health_check(db, health_result)

        return health_result

    def _save_health_check(self, db: Session, health_result: Dict[str, any]):
        """保存健康检查结果到数据库"""
        try:
            health_check = HealthCheck(
                service_id=health_result["service_id"],
                status=health_result["status"],
                response_time=health_result["response_time"],
                http_status=health_result["http_status"],
                error_message=health_result["error_message"]
            )
            db.add(health_check)
            db.commit()
        except Exception as e:
            logger.error(f"Failed to save health check result: {e}")
            db.rollback()

    async def check_all_services(self, db: Session) -> Dict[int, Dict[str, any]]:
        """检查所有激活的服务"""
        services = db.query(ServiceConfig).filter(ServiceConfig.is_active == True).all()
        results = {}

        # 并发检查所有服务
        tasks = [self.check_service_health(service, db) for service in services]
        health_results = await asyncio.gather(*tasks, return_exceptions=True)

        for service, result in zip(services, health_results):
            if isinstance(result, Exception):
                logger.error(f"Health check failed for service {service.name}: {result}")
                results[service.id] = {
                    "service_id": service.id,
                    "status": "error",
                    "error_message": str(result)
                }
            else:
                results[service.id] = result

        return results

    async def check_service_with_retry(
            self,
            service: ServiceConfig,
            db: Session,
            max_retries: int = None
    ) -> Tuple[bool, Dict[str, any]]:
        """带重试的健康检查"""
        if max_retries is None:
            max_retries = service.retry_count

        last_result = None

        for attempt in range(max_retries):
            result = await self.check_service_health(service, db)
            last_result = result

            if result["status"] == "healthy":
                return True, result

            if attempt < max_retries - 1:
                await asyncio.sleep(2 ** attempt)  # 指数退避

        return False, last_result

    async def close(self):
        """关闭HTTP客户端"""
        await self.client.aclose()


# 全局健康检查器实例
health_checker = HealthChecker()